package com.clippick.clipboard;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.util.Log;

import com.clippick.api.GetApi;
import com.clippick.common.GlobalValue;
import com.clippick.common.Toasts;
import com.clippick.database.ClipData;
import com.clippick.database.FeedOrderDBAdapter;
import com.clippick.database.HistoryDBAdapter;
import com.clippick.dbbusiness.DBBusiness;

public class PollingThread extends Thread {
	private PollingService mParent;
	private Handler mHandler;

	// system clipboard
	// private ClipboardManager systemClipboard;

	/** local database manager */
	//private HistoryDBAdapter dbManager;
	//private FeedOrderDBAdapter feedOrderDbManager;

	/** ClipPick Api */
	private GetApi api;

	String latestTimeFromServer = "";

	// sharedPreferences
	private SharedPreferences pref;
	// private SharedPreferences.Editor editor;

	// Socket.io newevent receiver that will update database and UI.
	// Added this receiver to fix the response from socket.io comes to intro.
	BroadcastReceiver checkSyncReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			Runnable process = new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					syncFeed();
				}
			};

			Thread thread = new Thread(process);
			thread.start();
		}
	};

	/* Constructor */
	public PollingThread() {
		super("Polling");
	}

	/** 서버에 저장된 클립과 안드로이드 내의 Database 클립이 다를 경우, 서버에서 가져온다. */
	public PollingThread(PollingService parent, Handler handler) {
		Log.i("pollingThread", "instance");
		mParent = parent;

		// api
		api = new GetApi(parent);
	}

	/** Cancel task */
	public void cancel() {
		interrupt();
	}

	@Override
	public void run() {
		syncFeed();
	}

	public static List<Map> convertArrayWithJSON(JSONArray array) {
		List<Map> result = new ArrayList<Map>();

		for (int i = 0; i < array.length(); i++) {
			Map<String, String> map = new HashMap<String, String>();
			try {
				String no = array.getJSONObject(i).getString("NO");
				String star = array.getJSONObject(i).getString("B_KEEP");
				map.put("NO", no);
				map.put("B_KEEP", star);
				result.add(map);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return result;
	}

	private void syncFeed() {
		try {
			// indicator is hiding by default
			showIndicator(false);

			// set time
			String response = "";
			String clientTime = GlobalValue.getUpdateTime(mParent);
			if (clientTime.equals("")) {
				clientTime = "0000-00-00 00:00:00";
			}

			// request
			String userId = GlobalValue.getUserEmail(mParent);
			String regKey = GlobalValue.getUserRegistrationKey(mParent);

			Log.i("polling", userId + ", " + regKey + ", " + clientTime);

			if (!userId.equals("") && !regKey.equals("")) {
				Log.i("longpoll", "request long-polling");
				response = api.longPolling(clientTime, userId, regKey);
			}

			// protect NullPointerException
			if (response != null && !response.equals("")) {
				try {
					JSONObject json = new JSONObject(response);
					String status = json.getString("status");
					Log.i("longpoll", "status : " + status);
					if (status.contains("Timeout")) {
						syncFeed();
					} else if (status.equals("success")) {
						if (mParent.getServiceContext() == null){
							/* non-sync mode: 
							 * 		stop listening from server
							 */
							return;
						}
						
						GlobalValue.IS_UPDATING = true;
						// send broadcast to show indicator on view
						showIndicator(true);

						// set latest time
						GlobalValue.setUpdateTime(mParent,
								json.getString("uptime"));

						// get feeds list
						JSONArray feed = json.getJSONArray("feed");
						compareLocalFeedsWithNewFeeds(convertArrayWithJSON(feed));

						// get star count
						JSONArray starCountArray = json.getJSONArray("star");
						JSONObject starCountObject = starCountArray
								.getJSONObject(0);

						// store star values into preferences
						GlobalValue.setMaxStarValue(
								mParent,
								Integer.parseInt(starCountObject.get(
										"star_total").toString()));
						GlobalValue.setStarCountValue(
								mParent,
								Integer.parseInt(starCountObject.get(
										"star_used").toString()));

						// let view update with latest data
						Intent updateViewIntent = new Intent();
						updateViewIntent
								.setAction("com.new.clippick.UpdatePolling");
						mParent.sendBroadcast(updateViewIntent);

						Intent starValueIntent = new Intent();
						starValueIntent.setAction("com.new.clippick.StarValue");
						mParent.sendBroadcast(starValueIntent);

						// stop indicator
						showIndicator(false);

						// call itself again to poll
						syncFeed();
					}

					PollingService.CONNECTED_COUNT = 0;

				} catch (JSONException ignored) {

				}
			}
		} catch (NullPointerException e) {
			e.printStackTrace();
		}
	}

	private void showIndicator(boolean value) {
		Intent indicatorIntent = new Intent();
		indicatorIntent.setAction("com.new.clippick.showIndicator");
		indicatorIntent.putExtra("show", value);
		mParent.sendBroadcast(indicatorIntent);
	}

	public void insertNewFeedIntoTempTable(List<Map> newFeed) {
		for (Map<String, String> item : newFeed) {
			// dbManager.insertFeedIntoTempTable(item);
			DBBusiness.insertFeedIntoTempTable(item);
		}
	}

	@SuppressWarnings("unchecked")
	public void compareLocalFeedsWithNewFeeds(List<Map> newFeed) {
		// DBBusiness.openDbs();

		this.insertNewFeedIntoTempTable(newFeed);

		Map<Object, Object> items = new HashMap<Object, Object>();
		items = DBBusiness.compareFeed();

		List<Map<?, Integer>> changedList = (List<Map<?, Integer>>) items
				.get("changed");
		List<Map<?, Integer>> newList = (List<Map<?, Integer>>) items
				.get("new");
		List<Map<?, Integer>> removedList = (List<Map<?, Integer>>) items
				.get("removed");

		Log.i("database", "changedList : " + changedList);
		Log.i("database", "newList : " + newList);
		Log.i("database", "removedList : " + removedList);

		if (!changedList.isEmpty()) {
			for (Map item : changedList) {
				Cursor c = DBBusiness.getFeedByIndex(Long.parseLong(item.get(
						"NO").toString()));

				if (c != null && c.moveToFirst()) {
					do {
						Map<String, String> m = new HashMap<String, String>();
						String starValue = c
								.getString(c
										.getColumnIndex(HistoryDBAdapter.KEY_DB_KEEPBOX));
						if (starValue.equals("0")) {
							starValue = "1";
						} else {
							starValue = "0";
						}
						m.put(HistoryDBAdapter.KEY_DB_KEEPBOX, starValue);
						m.put(HistoryDBAdapter.KEY_DB_DEVICE,
								c.getString(c
										.getColumnIndex(HistoryDBAdapter.KEY_DB_DEVICE)));
						m.put(HistoryDBAdapter.KEY_DB_CONTENTS,
								c.getString(c
										.getColumnIndex(HistoryDBAdapter.KEY_DB_CONTENTS)));
						m.put(HistoryDBAdapter.KEY_DB_DATE, c.getString(c
								.getColumnIndex(HistoryDBAdapter.KEY_DB_DATE)));
						m.put(HistoryDBAdapter.KEY_DB_TEXT, c.getString(c
								.getColumnIndex(HistoryDBAdapter.KEY_DB_TEXT)));
						m.put(HistoryDBAdapter.KEY_DB_IMG, c.getString(c
								.getColumnIndex(HistoryDBAdapter.KEY_DB_IMG)));
						m.put(HistoryDBAdapter.KEY_DB_NO, c.getString(c
								.getColumnIndex(HistoryDBAdapter.KEY_DB_NO)));

						// dbManager.updateFeed(m);
						DBBusiness.updateFeedFromHistoryDb(m);

						Log.i("changed", "" + m);

					} while (c.moveToNext());
				}
			}
		}

		// DBBusiness.closeDbs();

		if (!newList.isEmpty()) {
			StringBuffer stringBuffer = new StringBuffer();
			boolean isFirstItem = true;
			for (Map send : newList) {
				if (isFirstItem) {
					isFirstItem = false;
				} else {
					stringBuffer.append(",");
				}
				stringBuffer.append(send.get("NO").toString());
			}

			// request new feeds to server
			List<Map> newClipList = api.getnewClip(stringBuffer.toString());
			insertNewFeed(ClipData.getClipData(newClipList));
			Log.i("new", "" + stringBuffer.toString());
		}

		if (!removedList.isEmpty()) {
			for (Map item : removedList) {
				deleteLocalDb(Long.parseLong(item.get("NO").toString()));
				Log.i("deleted", "" + item);
			}
		}
	}

	// 이미지가 포함된 클립은 feedOrderDbManager에도 저장한다.
	private void insertNewFeed(List<Map> newClipList) {
		// DBBusiness.openDbs();

		for (Map clip : newClipList) {
			// new clip image data
			long index = Long.valueOf(clip.get("NO").toString());
			List<String> order = (List<String>) clip.get("HISTORY_IMG_ORDER");
			List<String> imagePath = (List<String>) clip
					.get("HISTORY_IMG_PATH");
			List<String> imageUrl = (List<String>) clip.get("HISTORY_IMG_URLS");

			long insertValue = DBBusiness.insertFeedFromHistory(clip);
			if (insertValue != -1) {
				int imageCursor = 0;
				int imageFileCount = imagePath.size();
				for (int i = 0; i < order.size(); ++i) {
					String strOrder = order.get(i);
					if (strOrder.contains("[img")
							&& (imageCursor < imageFileCount)) {
						DBBusiness.insertFeedFromFeedOrder(index,
								String.valueOf(i), imagePath.get(imageCursor),
								imageUrl.get(imageCursor));

						++imageCursor;
					} else {
						DBBusiness.insertFeedFromFeedOrder(index,
								String.valueOf(i), strOrder, "");
					}
				}
			}

			// new clip into systemClipboard
			mParent.setNewClip(clip.get(HistoryDBAdapter.KEY_DB_TEXT)
					.toString());
		}

		// DBBusiness.closeDbs();

		GlobalValue.IS_UPDATING = false;
	}

	private void deleteLocalDb(Long feedNo) {
		// DBBusiness.openDbs();

		Cursor c = DBBusiness.fetchFeedByIndex(feedNo);
		if (c == null)
			return;

		// 지울 데이터들의 image path
		List<String> filePathList = new ArrayList<String>();
		if (c.getCount() != 0) {
			do {
				String path = c
						.getString(c
								.getColumnIndexOrThrow(FeedOrderDBAdapter.KEY_DB_TEXT_PATH));
				if (path.contains(GlobalValue.CLIPPICK_IMAGE_ROOT_FOLDER)) {
					filePathList.add(path);
				}
			} while (c.moveToNext());
		}
		c.close();

		// dbManager.deleteFeed(feedNo);
		// feedOrderDbManager.deleteFeedByIndex(feedNo);
		DBBusiness.deleteFeedFromHistoryDb(feedNo);
		DBBusiness.deleteFeedFromFeedOrder(feedNo);

		for (String filePath : filePathList) {
			try {
				if (isImageInGallery(filePath)) {
					mParent.getContentResolver().delete(
							MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
							MediaStore.Images.ImageColumns.DATA + "='"
									+ filePath + "'", null);
				} else {
					File deleteFile = new File(filePath);
					deleteFile.delete();
				}

				mParent.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
						Uri.parse("file://"
								+ Environment.getExternalStorageDirectory())));
			} catch (Exception e) {
				e.printStackTrace();
				Toasts.showToast(mParent, "failed to delete image");
			}
		}

		// DBBusiness.closeDbs();
	}

	// check image exists
	private boolean isImageInGallery(String _Path) {
		String[] proj = { MediaStore.Images.Media._ID,
				MediaStore.Images.Media.DATA,
				MediaStore.Images.Media.DISPLAY_NAME,
				MediaStore.Images.Media.SIZE, MediaStore.Images.Media.TITLE };

		Cursor imageCursor = mParent.getContentResolver().query(
				MediaStore.Images.Media.EXTERNAL_CONTENT_URI, proj,
				MediaStore.Images.Media.DATA + "='" + _Path + "'", null, null);

		if (imageCursor != null && imageCursor.moveToFirst()) {
			if (imageCursor.getCount() > 0) {
				return true;
			}
		}
		imageCursor.close();
		return false;
	}
}
